from flask import request
from flask_restx import reqparse
from werkzeug import exceptions

from app.utils.exception import ValidationError


class Argument(reqparse.Argument):
    def handle_validation_error(self, error, bundle_errors):
        """
        Called when an error is raised while parsing. Aborts the request
        with a 400 status and an error message

        :param error: the error that was raised
        :param bool bundle_errors: do not abort when first error occurs, return a
            dict with the name of the argument and the error message to be
            bundled
        """
        error_str = str(error)
        error_msg = " ".join([str(self.help), error_str]) if self.help else error_str
        errors = {self.name: error_msg}
        raise ValidationError(errors=errors)


class CustomRequestParser(reqparse.RequestParser):
    """
    自定义参数校验错误相应结果
    """
    def __init__(self, *args, **kwargs):
        kwargs.update(argument_class=Argument)
        super().__init__(*args, **kwargs)

    def parse_args(self, req=None, strict=False):
        """
        Parse all arguments from the provided request and return the results as a ParseResult

        :param req:
        :param bool strict: if req includes args not in parser, throw 400 BadRequest exception
        :return: the parsed results as :class:`ParseResult` (or any class defined as :attr:`result_class`)
        :rtype: ParseResult
        """
        if req is None:
            req = request

        result = self.result_class()

        # A record of arguments not yet parsed; as each is found
        # among self.args, it will be popped out
        req.unparsed_arguments = (
            dict(self.argument_class("").source(req)) if strict else {}
        )
        errors = {}
        for arg in self.args:
            value, found = arg.parse(req, self.bundle_errors)
            if isinstance(value, ValueError):
                errors.update(found)
                found = None
            if found or arg.store_missing:
                result[arg.dest or arg.name] = value
        if errors:
            raise ValidationError(errors=errors)

        if strict and req.unparsed_arguments:
            arguments = ", ".join(req.unparsed_arguments.keys())
            msg = "Unknown arguments: {0}".format(arguments)
            raise exceptions.BadRequest(msg)

        return result
